#include "mainwindow.h"
#include <QFileDialog>
#include <QHostAddress>
#include <QMessageBox>
#include <QPushButton>
#include <QLineEdit>
#include <QProgressBar>
#include <QTextEdit>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QApplication>
#include <QFileInfo>
#include <QElapsedTimer>
#include <QDataStream>

// 全局辅助函数的定义
// 这两个函数的声明已移至 mainwindow.h
/**
 * @brief 格式化文件大小为可读字符串 (B, KB, MB, GB)。
 * @param bytes 文件大小（以字节为单位）。
 * @return 格式化后的文件大小字符串。
 */
QString formatFileSize(qint64 bytes) {
    if (bytes < 1024) { return QString::number(bytes) + " B"; }
    else if (bytes < 1024 * 1024) { return QString::number(bytes / 1024.0, 'f', 2) + " KB"; }
    else if (bytes < 1024 * 1024 * 1024) { return QString::number(bytes / (1024.0 * 1024.0), 'f', 2) + " MB"; }
    else { return QString::number(bytes / (1024.0 * 1024.0 * 1024.0), 'f', 2) + " GB"; }
}

/**
 * @brief 格式化传输速度为可读字符串 (B/s, KB/s, MB/s)。
 * @param bytes 传输的字节数。
 * @param milliseconds 传输所用的毫秒数。
 * @return 格式化后的传输速度字符串。
 */
QString formatSpeed(qint64 bytes, qint64 milliseconds) {
    if (milliseconds == 0) { return "0 B/s"; }
    double speedBps = (double)bytes * 1000.0 / milliseconds;
    if (speedBps < 1024) { return QString::number(speedBps, 'f', 2) + " B/s"; }
    else if (speedBps < 1024 * 1024) { return QString::number(speedBps / 1024.0, 'f', 2) + " KB/s"; }
    else { return QString::number(speedBps / (1024.0 * 1024.0), 'f', 2) + " MB/s"; }
}

/**
 * @brief MainWindow 类的构造函数。
 *
 * 初始化UI元素、成员变量并连接信号与槽。
 * @param parent 父QWidget指针，默认为nullptr。
 */
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    // 初始化列表的顺序应尽量与mainwindow.h中成员声明的顺序一致以避免 -Wreorder 警告
    , ui(nullptr)
    , udpSocket(new QUdpSocket(this))
    , file(nullptr) // 发送端文件指针
    // filePath (QString, 默认构造)
    , packetSize(8192)
    , totalPackets(0)
    , currentPacket(0)
    , timer(new QTimer(this)) // 发送端重传计时器
    // targetIP (QString, 默认构造)
    // targetPort (quint16, 默认构造或稍后赋值)
    , sendingFileActive(false)
    , outputFile_R(nullptr) // 接收端文件指针
    // outputFilePath_R (QString, 默认构造)
    , expectedTotalPackets_R(0)
    // receivedPacketIndices_R (QSet, 默认构造)
    , receivingFileActive(false)
    // UI 控件指针将在下面通过 new 创建，不在此处初始化
    , speedUpdateTimer(new QTimer(this)) // 速度更新计时器
    // transferDurationTimer (QElapsedTimer, 默认构造)
    , bytesSentInCurrentInterval(0) // 这个成员的初始化顺序警告需要关注
{
    this->setWindowTitle("UDP局域网内大文件互传系统（开源技术与应用课程项目）");
    this->resize(700, 580);

    QWidget *central = new QWidget(this);
    QVBoxLayout *mainLayout = new QVBoxLayout(central);
    mainLayout->setSpacing(10);
    mainLayout->setContentsMargins(15, 15, 15, 15);

    // --- UI 元素创建 ---
    QHBoxLayout *fileLayout = new QHBoxLayout;
    fileLayout->setSpacing(10);
    btnSelectFile = new QPushButton("选择文件", this);
    editFilePath = new QLineEdit(this);
    editFilePath->setPlaceholderText("文件路径将显示在这里");
    fileLayout->addWidget(btnSelectFile);
    fileLayout->addWidget(editFilePath, 1);

    QHBoxLayout *ipLayout = new QHBoxLayout;
    ipLayout->setSpacing(10);
    QLabel *labelIP = new QLabel("目标IP:", this);
    editTargetIP = new QLineEdit(this);
    editTargetIP->setPlaceholderText("例如: 192.168.1.100");
    QLabel *labelPort = new QLabel("端口:", this);
    editTargetPort = new QLineEdit(this);
    editTargetPort->setPlaceholderText("例如: 12345");
    editTargetPort->setFixedWidth(120);
    ipLayout->addWidget(labelIP);
    ipLayout->addWidget(editTargetIP, 1);
    ipLayout->addSpacing(10);
    ipLayout->addWidget(labelPort);
    ipLayout->addWidget(editTargetPort);

    QHBoxLayout *controlButtonLayout = new QHBoxLayout;
    controlButtonLayout->setSpacing(10);
    btnSend = new QPushButton("发送文件", this);
    btnReceive = new QPushButton("开始接收", this);
    btnCancelTransfer = new QPushButton("取消传输", this);
    btnCancelTransfer->setEnabled(false);

    controlButtonLayout->addStretch();
    controlButtonLayout->addWidget(btnSend);
    controlButtonLayout->addWidget(btnReceive);
    controlButtonLayout->addWidget(btnCancelTransfer);
    controlButtonLayout->addStretch();

    QHBoxLayout *infoLayout = new QHBoxLayout();
    infoLayout->setSpacing(10);
    labelFileSize = new QLabel("文件大小: -", this);
    labelTransferSpeed = new QLabel("速度: -", this);
    infoLayout->addWidget(labelFileSize, 1, Qt::AlignLeft);
    infoLayout->addWidget(labelTransferSpeed, 1, Qt::AlignRight);

    progressBar = new QProgressBar(this);
    textLog = new QTextEdit(this);
    textLog->setReadOnly(true);

    mainLayout->addLayout(fileLayout);
    mainLayout->addLayout(ipLayout);
    mainLayout->addLayout(controlButtonLayout);
    mainLayout->addLayout(infoLayout);
    mainLayout->addWidget(progressBar);
    mainLayout->addWidget(textLog, 1);
    setCentralWidget(central);

    btnSelectFile->setObjectName("btnSelectFile");
    btnSend->setObjectName("btnSend");
    btnReceive->setObjectName("btnReceive");
    btnCancelTransfer->setObjectName("btnCancelTransfer");
    labelFileSize->setObjectName("labelFileSize");
    labelTransferSpeed->setObjectName("labelTransferSpeed");

    // 应用样式表 (QSS)
    QString commonStyleSheet = QStringLiteral(
        "QMainWindow { background-color: #f4f4f4; }"
        "QPushButton { color: white; border: 1px solid #388E3C; padding: 8px 15px; border-radius: 4px; min-height: 22px; font-weight: bold; }"
        "QPushButton#btnSelectFile { background-color: #0277BD; border-color: #01579B; }"
        "QPushButton#btnSelectFile:hover { background-color: #0288D1; }"
        "QPushButton#btnSelectFile:pressed { background-color: #01579B; }"
        "QPushButton#btnSend, QPushButton#btnReceive { background-color: #4CAF50; border-color: #388E3C; }"
        "QPushButton#btnSend:hover, QPushButton#btnReceive:hover { background-color: #45a049; }"
        "QPushButton#btnSend:pressed, QPushButton#btnReceive:pressed { background-color: #3e8e41; }"
        "QPushButton#btnCancelTransfer { background-color: #f44336; border-color: #D32F2F; color: white; }"
        "QPushButton#btnCancelTransfer:hover { background-color: #E53935; }"
        "QPushButton#btnCancelTransfer:pressed { background-color: #C62828; }"
        "QPushButton:disabled { background-color: #BDBDBD; border-color: #9E9E9E; color: #757575; }"
        "QLineEdit { padding: 6px; border: 1px solid #ccc; border-radius: 4px; background-color: #ffffff; font-size: 13px; }"
        "QProgressBar { border: 1px solid #c1c1c1; border-radius: 4px; text-align: center; background-color: #e0e0e0; height: 24px; font-weight: bold; }"
        "QProgressBar::chunk { background-color: #4CAF50; border-radius: 3px; margin: 1px; }"
        "QTextEdit { border: 1px solid #ccc; border-radius: 4px; padding: 6px; background-color: #ffffff; font-family: 'Courier New', Courier, monospace; font-size: 13px; }"
        "QLabel { padding: 2px; font-weight: normal; font-size: 13px; }"
        "QLabel#labelFileSize, QLabel#labelTransferSpeed { font-weight: bold; }"
        );
    this->setStyleSheet(commonStyleSheet);

    // --- 连接信号与槽 ---
    connect(btnSelectFile, &QPushButton::clicked, this, &MainWindow::on_btnSelectFile_clicked);
    connect(btnSend, &QPushButton::clicked, this, &MainWindow::on_btnSend_clicked);
    connect(btnReceive, &QPushButton::clicked, this, &MainWindow::on_btnReceive_clicked);
    connect(btnCancelTransfer, &QPushButton::clicked, this, &MainWindow::on_btnCancelTransfer_clicked);
    connect(udpSocket, &QUdpSocket::readyRead, this, &MainWindow::onReadyRead);
    connect(timer, &QTimer::timeout, this, &MainWindow::onTimeout);
    connect(speedUpdateTimer, &QTimer::timeout, this, &MainWindow::updateTransferSpeedDisplay);
}

/**
 * @brief MainWindow 类的析构函数。
 *
 * 清理定时器、文件句柄等资源。
 */
MainWindow::~MainWindow()
{
    if (timer->isActive()) timer->stop();
    if (speedUpdateTimer->isActive()) speedUpdateTimer->stop();
    if (file && file->isOpen()) {
        file->close();
    }
    delete file;
    file = nullptr;

    if (outputFile_R) {
        if (outputFile_R->isOpen()) {
            outputFile_R->close();
        }
        delete outputFile_R;
        outputFile_R = nullptr;
    }
    // delete ui; // 如果 ui 为 nullptr，则不需要 delete
}

/**
 * @brief 重置UI元素到空闲状态。
 *
 * 将进度条、速度标签等恢复到初始未传输状态。
 */
void MainWindow::resetUiToIdleState() {
    updateUiForActiveTransfer(false);
    progressBar->setValue(0);
    labelTransferSpeed->setText("速度: -");
}

/**
 * @brief 根据传输是否活动来更新UI控件的启用/禁用状态。
 * @param active 布尔值，true表示传输正在进行，false表示传输已停止或未开始。
 */
void MainWindow::updateUiForActiveTransfer(bool active) {
    btnSend->setEnabled(!active);
    btnReceive->setEnabled(!active);
    btnSelectFile->setEnabled(!active);
    editTargetIP->setEnabled(!active);
    editTargetPort->setEnabled(!active);
    btnCancelTransfer->setEnabled(active);
}

/**
 * @brief 清理发送端的状态和资源。
 * @param transferCompletedSuccessfully 布尔值，指示传输是否成功完成。
 * 如果为false，通常表示用户取消或发生错误。
 */
void MainWindow::cleanupSenderState(bool transferCompletedSuccessfully) {
    if (timer->isActive()) timer->stop();
    if (speedUpdateTimer->isActive()) speedUpdateTimer->stop();
    if (file && file->isOpen()) {
        file->close();
    }
    sendingFileActive = false;
    if (!transferCompletedSuccessfully) {
        textLog->append("发送操作已中止。");
    }
    resetUiToIdleState();
}

/**
 * @brief 清理接收端的状态和资源。
 * @param transferCompletedSuccessfully 布尔值，指示传输是否成功完成。
 * @param userCancelled 布尔值，指示是否是用户主动取消了接收。如果为true，会尝试删除未完成的文件。
 */
void MainWindow::cleanupReceiverState(bool transferCompletedSuccessfully, bool userCancelled) {
    receivingFileActive = false;
    if (outputFile_R) {
        if (outputFile_R->isOpen()) {
            outputFile_R->flush();
            outputFile_R->close();
        }
        if (userCancelled && !outputFilePath_R.isEmpty()) {
            if (QFile::exists(outputFilePath_R)) {
                if (QFile::remove(outputFilePath_R)) {
                    textLog->append("接收被取消，临时文件 '" + outputFilePath_R + "' 已删除。");
                } else {
                    textLog->append("接收被取消，但删除临时文件 '" + outputFilePath_R + "' 失败。错误: " + outputFile_R->errorString());
                }
            } else if (userCancelled) {
                textLog->append("接收被取消，未创建文件。");
            }
        }
        delete outputFile_R;
        outputFile_R = nullptr;
    }
    outputFilePath_R.clear();
    expectedTotalPackets_R = 0;
    receivedPacketIndices_R.clear();
    if (userCancelled) {
        textLog->append("接收操作已取消。");
    } else if (!transferCompletedSuccessfully) {
        textLog->append("接收操作已中止或发生错误。");
    }
    resetUiToIdleState();
}

/**
 * @brief “选择文件”按钮点击事件处理槽函数。
 *
 * 弹出文件对话框供用户选择要发送的文件。
 * 更新UI显示文件路径和大小。
 */
void MainWindow::on_btnSelectFile_clicked()
{
    if (sendingFileActive || receivingFileActive) return;
    QString path = QFileDialog::getOpenFileName(this, "选择要发送的文件");
    if (!path.isEmpty()) {
        filePath = path;
        editFilePath->setText(path);
        textLog->append("已选择文件: " + path);
        QFile tempFileObj(filePath);
        if (tempFileObj.open(QIODevice::ReadOnly)) {
            qint64 fileSize = tempFileObj.size();
            labelFileSize->setText(QString("文件大小: %1").arg(formatFileSize(fileSize))); // 使用全局声明的 formatFileSize
            tempFileObj.close();
        } else {
            labelFileSize->setText("文件大小: 无法读取");
            QMessageBox::warning(this, "错误", "无法读取所选文件的大小。");
        }
        labelTransferSpeed->setText("速度: -");
        progressBar->setValue(0);
    }
}

/**
 * @brief “取消传输”按钮点击事件处理槽函数。
 *
 * 如果当前正在发送文件，则停止发送并清理发送端状态。
 * 如果当前正在接收文件，则停止接收并清理接收端状态。
 */
void MainWindow::on_btnCancelTransfer_clicked() {
    if (sendingFileActive) {
        textLog->append("正在取消发送...");
        cleanupSenderState(false);
    } else if (receivingFileActive) {
        textLog->append("正在取消接收...");
        cleanupReceiverState(false, true);
    }
}

/**
 * @brief UDP套接字收到数据时的处理槽函数 (网络数据分发)。
 *
 * 当udpSocket接收到数据报时被调用。
 * 根据数据报内容判断是ACK包还是数据包，并分发给相应的处理函数。
 */
void MainWindow::onReadyRead()
{
    while (udpSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;
        udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

        if (datagram.startsWith("ACK:")) {
            if (!sendingFileActive) {
                continue;
            }
            handleAckPacket(datagram, sender, senderPort);
        } else {
            if (!receivingFileActive) {
                continue;
            }
            handleReceiveData(datagram, sender, senderPort);
        }
    }
}